from vcbrowser.engine.base import BaseEngine, EngineError
from vcbrowser.nodes import Node, FileNode, DirNode
from vcbrowser.enumerations import NodeKind, RepositoryType

import pysvn
import time
import datetime
import logging
import posixpath

class SubversionEngine(BaseEngine):
    """
    Will use default config directory of subversion.
    As so it is needed that system has stored passwords
    to target remote repository first.
    """

    head_revision = pysvn.Revision(pysvn.opt_revision_kind.head)
    
    def __init__(self, repo_url):
        super(SubversionEngine, self).__init__(repo_url)
        # TODO: Should be more flexible to choose config dir
        self.client = pysvn.Client()
        self.handler = SubversionEntryHandler()

        # pysvn specific
        self.entry_list = []

    def _get_revision(self, revision=None):
        """
        If pysvn.Revision is passed then it is simply returned
        without any changes.
        If revision is an integer, then it should be converted
        into proper pysvn.Revision object and returned.
        If revision is None, head_revision is returned.
        """
        if isinstance(revision, type(SubversionEngine.head_revision)):
            return revision
        elif isinstance(revision, int):
            return pysvn.Revision(pysvn.opt_revision_kind.number, number)
        else:
            return SubversionEngine.head_revision

    def _get_entry_list(self, url, revision):
        revision = self._get_revision(revision)
        entry_list = self.client.ls(url, revision)
        return entry_list
    
    def _transform_info(self, data):
        """
        PysvnEntry object has data dict which contains:
          created_rev -> needs only value of number attr
          time -> needs to convert into datetime object
          size -> in bytes so its all ok
          last_author -> as unicode so no work here too
        """
        transformed = data.copy()
        transformed['revision'] = data['created_rev'].number
        
        try:
            t = data['time'] - time.altzone
            transformed['created_at'] = (datetime.datetime(1970,1,1) + 
                                datetime.timedelta(seconds=t) )
        except KeyError, err:
            if transformed.haskey('created_at'):
                pass
            raise err
        
        return transformed
        
    def request(self, url, revision=None, fetch_content=True):
        """
        Pass revision as int.
        """
        full_url = Node.join_url(self.repo_url, url)
        revision = self._get_revision(revision)
        try:
            entry = self.client.list(full_url, revision)[0][0]
        except pysvn.ClientError, err:
            raise EngineError("Couldn't check kind at '%s'\nError was:\n%s" % (full_url, err))
        kind = self.handler.get_kind(entry)
        
        if kind is NodeKind.FILE:
            if fetch_content:
                content = self.get_file_content(url, revision)
            else:
                content = None
            requested_node = FileNode(url, content)
        elif kind is NodeKind.DIR:
            nodes = self.fetch_nodes(url, revision)
            dirs = [node for node in nodes if node.is_dir()]
            files = [node for node in nodes if node.is_file()]
            nodes = dirs + files
            requested_node = DirNode(url, nodes)

        return requested_node

    def fetch_nodes(self, url, revision=None):
        full_url = Node.join_url(self.repo_url, url)
        #logging.info("Fetching dir for: %s" % url)
        revision = self._get_revision(revision)
        entry_list = self._get_entry_list(full_url, revision.number)
        nodes = []
        for entry in entry_list:
            kind = self.handler.get_kind(entry)
            node_name = Node.get_name(entry.name)
            node_url = Node.join_url(url, node_name)
            if kind is NodeKind.FILE:
                # We don't set content as this node is fetched
                # for information purposes
                node = FileNode(node_url, content=None)
            else:
                # We don't set nodes
                node = DirNode(node_url, nodes=[])
            node.data = self._transform_info(entry.data)
            nodes.append(node)
        return nodes
    
    def get_file_content(self, url, revision=None):
        full_url = Node.join_url(self.repo_url, url)
        revision = self._get_revision(revision)
        try:
            content = self.client.cat(full_url, revision)
        except pysvn.ClientError, err:
            raise EngineError("Couldn't retrieve content of the file. Error was:\n%s" % str(err))
            content = ''
        return content

    def walk(self, topurl, revision=None, fetch_files=False):
        """
        Similar to os.walk method. Insted of filesystem
        it walks through repository of the given topurl.
        Returns list of tuples (topnode, dirnodes, filenodes).
        """
        revision = self._get_revision(revision)
        try:
            topnode = self.request(topurl, revision, fetch_content=fetch_files)
            if fetch_files:
                for filenode in topnode.files:
                    filenode.content = self.get_file_content(filenode.url, revision)
            yield (topnode, topnode.dirs, topnode.files)
            for dirnode in topnode.dirs:
                for tup in self.walk(dirnode.url, revision, fetch_files):
                    yield tup
        except EngineError, err:
            # TODO: What to do with this exception? Probably kind was not retrieved...
            yield (),(),()

        

class SubversionEntryHandler(object):
    """
    Handler for subversion specific entry objects
    returned by 'pysvn' library.
    """

    def __init__(self, client=None):
        self.client = client

    def get_kind(self, entry):
        if entry.kind == pysvn.node_kind.dir:
            kind = NodeKind.DIR
        else:
            kind = NodeKind.FILE
        return kind

    def is_dir(self, entry):
        kind = self.get_kind(entry)
        return kind == NodeKind.DIR

    def is_file(self, entry):
        kind = self.get_kind(entry)
        return kind == NodeKind.FILE


    




